﻿using System;
using System.Xml;
namespace GeoSketch2.Core
{
    /// <summary>
    /// The 2D point class for the geosketch application, coordinates are integers
    /// </summary>
    [Serializable]
    public class Point2D32S
    {
        #region Fields
        /// <value name="X">The X-coordinate of the point</value>
        /// <value name="Y">The Y-coordinate of the point</value>
        public int X, Y; 
        #endregion

        #region Constructor
        /// <summary>
        /// 2D Point with an integer precision
        /// </summary>
        public Point2D32S()
        {
            X = 0;
            Y = 0;
        }

        /// <summary>
        /// 2D Point with an integer precision
        /// </summary>
        /// <param name="X">The X-coordinate of the point</param>        
        /// <param name="Y">The Y-coordinate of the point</param>        
        public Point2D32S(int X, int Y)
        {
            this.X = X;
            this.Y = Y;
        } 
        #endregion

        #region Operators
        /// <summary>
        /// Adds Point2 to Point1
        /// </summary>
        /// <param name="Point1">First point</param>
        /// <param name="Point2">Second point</param>
        public static Point2D32S operator +(Point2D32S Point1, Point2D32S Point2)
        {
            return new Point2D32S(Point1.X + Point2.X, Point1.Y + Point2.Y);
        }

        /// <summary>
        /// Negates the point
        /// </summary>
        /// <param name="Point">The point to negate</param>
        public static Point2D32S operator -(Point2D32S Point)
        {
            return new Point2D32S(-Point.X, -Point.Y);
        } 
        #endregion
    }

    /// <summary>
    /// The 2D point class for the geosketch application, coordinates are single precision numbers (float or single)
    /// </summary>
    [Serializable]
    public class Point2D32F
    {
        #region Fields
        /// <value name="X">The X-coordinate of the point</value>
        /// <value name="Y">The Y-coordinate of the point</value>
        public float X, Y; 
        #endregion

        #region Constructor
        /// <summary>
        /// 2D Point with an integer precision
        /// </summary>
        public Point2D32F()
        {
            X = 0;
            Y = 0;
        }

        /// <summary>
        /// 2D Point with a single precision
        /// </summary>
        /// <param name="X">The X-coordinate of the point</param>        
        /// <param name="Y">The Y-coordinate of the point</param>        
        public Point2D32F(float X, float Y)
        {
            this.X = X;
            this.Y = Y;
        } 
        #endregion

        #region Operators
        /// <summary>
        /// Adds Point2 to Point1
        /// </summary>
        /// <param name="Point1">First point</param>
        /// <param name="Point2">Second point</param>
        public static Point2D32F operator +(Point2D32F Point1, Point2D32F Point2)
        {
            return new Point2D32F(Point1.X + Point2.X, Point1.Y + Point2.Y);
        }

        /// <summary>
        /// Negates the point
        /// </summary>
        /// <param name="Point">The point to negate</param>
        public static Point2D32F operator -(Point2D32F Point)
        {
            return new Point2D32F(-Point.X, -Point.Y);
        } 
        #endregion
    }

    /// <summary>
    /// The 2D point class for the geosketch application, coordinates are double precision numbers (double)
    /// </summary>
    [Serializable]
    public class Point2D64D:BaseGeosketchObject
    {
        #region Fields
        /// <summary>
        /// The X-coordinate of the upper left corner
        /// </summary>
        public double X;
        /// <summary>
        /// The Y-coordinate of the upper left corner
        /// </summary>
        public double Y; 
        #endregion

        #region Constructor
        /// <summary>
        /// 2D Point with a double precision
        /// </summary>
        public Point2D64D()
        {
            X = 0;
            Y = 0;
        }

        /// <summary>
        /// 2D Point with a double precision
        /// </summary>
        /// <param name="X">The X-coordinate of the point</param>        
        /// <param name="Y">The Y-coordinate of the point</param>        
        public Point2D64D(double X, double Y)
        {
            this.X = X;
            this.Y = Y;
        }

        /// <summary>
        /// 2D Point with a double precision
        /// </summary>
        /// <param name="Xr">The XmlReader to read from</param> 
        public Point2D64D(XmlReader Xr)
        {
            string[] LineSplit;

            LineSplit = Xr.ReadString().Split(Vars.Setup.GeoSketch.CoordinatesSeparator);

            this.X = Convert.ToDouble(LineSplit[0], Vars.Setup.GeoSketch.NumberFormat);
            this.Y = Convert.ToDouble(LineSplit[1], Vars.Setup.GeoSketch.NumberFormat);
        }
        #endregion

        #region Operators
        /// <summary>
        /// Adds Point2 to Point1
        /// </summary>
        /// <param name="Point1">First point</param>
        /// <param name="Point2">Second point</param>
        public static Point2D64D operator +(Point2D64D Point1, Point2D64D Point2)
        {
            return new Point2D64D(Point1.X + Point2.X, Point1.Y + Point2.Y);
        }

        /// <summary>
        /// Substract Point2 to Point1
        /// </summary>
        /// <param name="Point1">First point</param>
        /// <param name="Point2">Second point</param>
        public static Point2D64D operator -(Point2D64D Point1, Point2D64D Point2)
        {
            return new Point2D64D(Point1.X - Point2.X, Point1.Y - Point2.Y);
        }

        /// <summary>
        /// Negates the point
        /// </summary>
        /// <param name="Point">The point to negate</param>
        public static Point2D64D operator -(Point2D64D Point)
        {
            return new Point2D64D(-Point.X, -Point.Y);
        }

        /// <summary>
        /// Multiply a point by a scalar
        /// </summary>
        /// <param name="Point">First point</param>
        /// <param name="Scalar">Second point</param>
        public static Point2D64D operator *(double Scalar,Point2D64D Point)
        {
            return new Point2D64D(Point.X * Scalar, Point.Y * Scalar);
        }

        /// <summary>
        /// Multiply a point by a scalar
        /// </summary>
        /// <param name="Point">First point</param>
        /// <param name="Scalar">Second point</param>
        public static Point2D64D operator *( Point2D64D Point,double Scalar)
        {
            return Scalar * Point;
        }

        /// <summary>
        /// Divides a point by a scalar
        /// </summary>
        /// <param name="Point">First point</param>
        /// <param name="Scalar">Second point</param>
        public static Point2D64D operator /( Point2D64D Point,double Scalar)
        {
            return Point * (1 / Scalar);
        }

        /// <summary>
        /// Converts a point with integer coordinates to a point with double precision coordinates
        /// </summary>
        /// <param name="Point">Source point</param>
        public static implicit operator Point2D64D(Point2D32S Point)
        {
            return new Point2D64D(Point.X, Point.Y);
        }

        /// <summary>
        /// Converts a point with single precision coordinates to a point with double precision coordinates
        /// </summary>
        /// <param name="Point">Source point</param>
        public static implicit operator Point2D64D(Point2D32F Point)
        {
            return new Point2D64D(Point.X, Point.Y);
        }
        
        /// <summary>
        /// Converts a point with integer coordinates to a point with double precision coordinates
        /// </summary>
        /// <param name="Point">Source point</param>
        public static implicit operator Point2D64D(System.Drawing.Point Point)
        {
            return new Point2D64D(Point.X, Point.Y);
        }

        /// <summary>
        /// Converts a point with double precision coordinates to a point with integer coordinates
        /// </summary>
        /// <param name="Point">Source point</param>
        public static implicit operator System.Drawing.Point(Point2D64D Point)
        {
            return new System.Drawing.Point((int)Math.Round(Point.X),(int)Math.Round(Point.Y));
        }

        /// <summary>
        /// Converts a point with integer coordinates to a point with double precision coordinates
        /// </summary>
        /// <param name="Point">Source point</param>
        public static implicit operator Point2D64D(System.Drawing.PointF Point)
        {
            return new Point2D64D(Point.X, Point.Y);
        }

        /// <summary>
        /// Converts a point with double precision coordinates to a point with single precision coordinates
        /// </summary>
        /// <param name="Point">Source point</param>
        public static implicit operator System.Drawing.PointF(Point2D64D Point)
        {
            return new System.Drawing.PointF((float)Point.X, (float)Point.Y);
        }

        /// <summary>
        /// Converts a point with integer coordinates to a point with double precision coordinates
        /// </summary>
        /// <param name="Point">Source point</param>
        public static implicit operator Point2D32S(Point2D64D Point)
        {
            return new Point2D32S((int)(Math.Round(Point.X)), (int)(Math.Round(Point.Y)));
        }
        #endregion 

        #region Methods
        /// <summary>
        /// Return a string version of the point
        /// </summary>
        public override string ToString()
        {
            return "Point2D64D(" + X.ToString() + "," + Y.ToString() + ")";
        }

        /// <summary>
        /// Writes the object to a file
        /// </summary>
        /// <param name="Xw">The XmlWriter to write to</param> 
        public void ToFile(XmlWriter Xw)
        {
            Xw.WriteStartElement(this.GetType().FullName);
            Xw.WriteString(this.XMLString);
            Xw.WriteEndElement();
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets the XML string representation of the object
        /// </summary>
        string XMLString
        {
            get
            {
                return X.ToString(Vars.Setup.GeoSketch.NumberFormat) + Vars.Setup.GeoSketch.CoordinatesSeparator + Y.ToString(Vars.Setup.GeoSketch.NumberFormat);
            }
        } 
        #endregion
    }
}
